namespace SoundFingerprinting.FFT
{
    using System;

    using SoundFingerprinting.Configuration;

    internal class LogUtility : ILogUtility
    {
        public ushort[] GenerateLogFrequenciesRanges(int sampleRate, SpectrogramConfig configuration)
        {
            if (configuration.UseDynamicLogBase)
            {
                return GenerateLogFrequenciesDynamicBase(sampleRate, configuration);
            }

            return GenerateStaticLogFrequencies(sampleRate, configuration);
        }

        /// <summary>
        ///   Gets the index in the spectrum vector from according to the starting frequency specified as the parameter
        /// </summary>
        /// <param name = "frequency">Frequency to be found in the spectrum vector [E.g. 300Hz]</param>
        /// <param name = "sampleRate">Frequency rate at which the signal was processed [E.g. 5512Hz]</param>
        /// <param name = "spectrumLength">Length of the spectrum [2048 elements generated by WDFT from which only 1024 are with the actual data]</param>
        /// <returns>Index of the frequency in the spectrum array</returns>
        /// <remarks>
        /// The Bandwidth of the spectrum runs from 0 until SampleRate / 2 [E.g. 5512 / 2]
        ///   Important to remember:
        ///   N points in time domain correspond to N/2 + 1 points in frequency domain
        ///   E.g. 300 Hz applies to 112'th element in the array
        /// </remarks>
        public ushort FrequencyToSpectrumIndex(float frequency, int sampleRate, int spectrumLength)
        {
            float fraction = frequency / ((float)sampleRate / 2); // N sampled points in time correspond to [0, N/2] frequency range
            int index = (int)Math.Round(((spectrumLength / 2) + 1) * fraction); // DFT N points defines [N/2 + 1] frequency points
            return (ushort)index;
        }

        private ushort[] GenerateLogFrequenciesDynamicBase(int sampleRate, SpectrogramConfig configuration)
        {
            double logBase = Math.Exp(Math.Log((float)configuration.FrequencyRange.Max / configuration.FrequencyRange.Min) / configuration.LogBins);
            double mincoef = (float)configuration.WdftSize / sampleRate * configuration.FrequencyRange.Min;
            ushort[] indexes = new ushort[configuration.LogBins + 1];
            for (int j = 0; j < configuration.LogBins + 1; j++)
            {
                int start = (int)((Math.Pow(logBase, j) - 1.0) * mincoef);
                indexes[j] = (ushort)(start + (int)mincoef);
            }

            return indexes;
        }

        private ushort[] GenerateStaticLogFrequencies(int sampleRate, SpectrogramConfig configuration)
        {
            double logMin = Math.Log(configuration.FrequencyRange.Min, configuration.LogBase);
            double logMax = Math.Log(configuration.FrequencyRange.Max, configuration.LogBase);

            double delta = (logMax - logMin) / configuration.LogBins;

            ushort[] indexes = new ushort[configuration.LogBins + 1];
            double accDelta = 0;
            for (int i = 0; i <= configuration.LogBins; ++i)
            {
                float freq = (float)Math.Pow(configuration.LogBase, logMin + accDelta);
                accDelta += delta;

                indexes[i] = FrequencyToSpectrumIndex(freq, sampleRate, configuration.WdftSize); // Find the start index in array from which to start the summation
            }

            return indexes;
        }
    }
}